﻿#include "TextTexturePool.h"
#include "Font/Glyph.h"
#include "Texture/Texture2D.h"
#include "Font/TextPoolMacros.h"


//将期望的字符大小转换为实际的字符大小
int32_t ScaleSize(int32_t InSize)
{
    //实际占用的纹理大小至少比字符大小大1，这是为了避免采样时字符之间的重叠
    if(InSize<TEXTURE_SIZE_0)
    {
        return TEXTURE_SIZE_0;
    }
    else if(InSize<TEXTURE_SIZE_1)
    {
        return TEXTURE_SIZE_1;
    }
    else if(InSize<TEXTURE_SIZE_2)
    {
        return TEXTURE_SIZE_2;
    }
    else if(InSize<TEXTURE_SIZE_3)
    {
        return TEXTURE_SIZE_3;
    }
    else if(InSize<TEXTURE_SIZE_4)
    {
        return TEXTURE_SIZE_4;
    }
    else if(InSize<TEXTURE_SIZE_5)
    {
        return TEXTURE_SIZE_5;
    }
    else if(InSize<=TEXTURE_SIZE_6) 
    {
        return TEXTURE_SIZE_6;
    }
    else
    {
        assert(false);
        return -1;
    }
}

//将实际的字符大小转换为纹理的索引
int32_t GetNodesIndex(int32_t InSize)
{
    switch(InSize)
    {
        case TEXTURE_SIZE_0:
            return 0;
        case TEXTURE_SIZE_1:
            return 1;
        case TEXTURE_SIZE_2:
            return 2;
        case TEXTURE_SIZE_3:
            return 3;
        case TEXTURE_SIZE_4:
            return 4;
        case TEXTURE_SIZE_5:
            return 5;
        case TEXTURE_SIZE_6:
            return 6;
    };

    assert(false);
    return -1;
}

RTextTexturePool::RTextTexturePool()
{
    //初始化空闲的纹理节点
    EmptyNodes.Resize(7);
}

int32_t RTextTexturePool::GetTextureSize()
{
    return TEXTURE_IN_POOL_SIZE;
}

SCharTexture RTextTexturePool::FindOrCreate(std::shared_ptr<SFreeTypeGlyph> InCharGlyph)
{
    //查找字符纹理
    auto Found=CharMap.Contains(InCharGlyph);
    if(Found)
    {
        return CharMap[InCharGlyph];
    }

    //创建字符纹理
    SCharTexture CharTexture;
    CharTexture.FontSize=InCharGlyph->FontSize;
    CharTexture.Width=InCharGlyph->Box.Extent.X;
    CharTexture.Height=InCharGlyph->Box.Extent.Y;

    //查找空闲的纹理,
    auto OcuppiedSize=ScaleSize(CharTexture.Width);
    auto NodeIndex=GetNodesIndex(OcuppiedSize);
    //查找空闲的纹理节点
      
    auto Node=Ocuppy(OcuppiedSize);
    assert(Node);
    CharTexture.Texture=Node->Texture;
    CharTexture.RawWidth=InCharGlyph->Box.Extent.X;
    CharTexture.RawHeight=InCharGlyph->Box.Extent.Y;
    CharTexture.X=Node->X;
    CharTexture.Y=Node->Y;

    //添加字符纹理
    CharMap.Add(InCharGlyph,CharTexture);

    return CharTexture;
}

std::shared_ptr<CTextureTreeNode> RTextTexturePool::Ocuppy(uint16_t InSize)
{
    assert(InSize<=TEXTURE_IN_POOL_SIZE);

    auto NodeIndex=GetNodesIndex(InSize);
    //查找空闲的纹理节点
    if(!EmptyNodes[NodeIndex].empty())
    {
        auto Node=EmptyNodes[NodeIndex].back();
        EmptyNodes[NodeIndex].pop_back(); //移除空闲节点
        auto Result=OcuppyWithNode(InSize,Node);
        assert(Result);//由于是从相等大小的纹理中切割出来的，所以一定能够分割成功
        return Result;
    }
    else
    {
        //没有空闲的纹理节点，尝试分割更大的纹理
        for(int32_t i=NodeIndex+1;i<EmptyNodes.Num();++i)
        {
            if(!EmptyNodes[i].empty())
            {
                auto Node=EmptyNodes[i].back();
                EmptyNodes[i].pop_back(); //移除空闲节点
                auto Result=OcuppyWithNode(InSize,Node);
                assert(Result);//由于是从更大的纹理中切割出来的，所以一定能够分割成功
                return Result;
            }
        }
        

    }
        
    //没有足够大的纹理，需要创建新的纹理
    GenerateBigTexture();
    //再次尝试分割
    return Ocuppy(InSize);
}

std::shared_ptr<CTextureTreeNode> RTextTexturePool::OcuppyWithNode(
    uint16_t InSize
    , std::shared_ptr<CTextureTreeNode> InNode)
{
    assert(InNode);
    assert(InNode);
    assert(InNode->Texture);
    assert(InNode->Size>=InSize);
    assert(InSize>=TEXTURE_SIZE_0 && InSize<=TEXTURE_SIZE_3);



    //如果节点大小刚好，直接返回
    if(InNode->Size==InSize )
    {
        if(!InNode->SubArea[0] 
            && !InNode->SubArea[1] 
            && !InNode->SubArea[2] 
            && !InNode->SubArea[3])
        {
            //节点完全空闲并且大小刚好
            return InNode;
        }
        else
        {
            //节点不是完全空闲，无法分割出InSize大小的节点
            return nullptr;
        }
    }

    assert(InNode->Size>InSize);

    //如果节点大小大于InSize，那么尝试分割节点并从更小的节点中分割出InSize大小的节点
    if(EmptyNodes[GetNodesIndex(InNode->Size/2)].empty())
    {
        //没有小一号的节点，需要创建
        for(int32_t i=0;i<4;++i)
        {
            if(!InNode->SubArea[i])
            {
                InNode->SubArea[i]=std::make_shared<CTextureTreeNode>();
                InNode->SubArea[i]->Size=InNode->Size/2;
                InNode->SubArea[i]->Parent=InNode;
                InNode->SubArea[i]->Texture=InNode->Texture;

                switch(i)
                {
                    case 0:
                        InNode->SubArea[i]->X=InNode->X;
                        InNode->SubArea[i]->Y=InNode->Y;
                        break;
                    case 1:
                        InNode->SubArea[i]->X=InNode->X;
                        InNode->SubArea[i]->Y=InNode->Y+InNode->Size/2;
                        break;
                    case 2:
                        InNode->SubArea[i]->X=InNode->X+InNode->Size/2;
                        InNode->SubArea[i]->Y=InNode->Y;
                        break;
                    case 3:
                        InNode->SubArea[i]->X=InNode->X+InNode->Size/2;
                        InNode->SubArea[i]->Y=InNode->Y+InNode->Size/2;
                        break;
                }
                EmptyNodes[GetNodesIndex(InNode->SubArea[i]->Size)].push_back(InNode->SubArea[i]);
            }
        }
    }

    //尝试从小一号的节点中分割出InSize大小的节点
    if(EmptyNodes[GetNodesIndex(InNode->Size/2)].empty())
    {
        //子节点中也没有空闲的节点，分割失败
        return nullptr;
    }
    
    auto SmallerNode=EmptyNodes[GetNodesIndex(InNode->Size/2)].back();
    EmptyNodes[GetNodesIndex(InNode->Size/2)].pop_back();
    return OcuppyWithNode(InSize,SmallerNode);
}

void RTextTexturePool::GenerateBigTexture()
{
    //初始化空闲的纹理节点
    auto Node=std::make_shared<CTextureTreeNode>();
    Node->Size=TEXTURE_IN_POOL_SIZE;
    EmptyNodes[GetNodesIndex(TEXTURE_IN_POOL_SIZE)].push_back(Node);

    //添加一张空的纹理
    TVector<uint8_t> EmptyData;
    EmptyData.Resize(TEXTURE_IN_POOL_SIZE*TEXTURE_IN_POOL_SIZE*1);
    Node->Texture=RTexture2D::CreateFromRawData(
        std::move(EmptyData),
        TEXTURE_IN_POOL_SIZE,
        SIntPoint(TEXTURE_IN_POOL_SIZE,TEXTURE_IN_POOL_SIZE),
        ETextureFormat::R8,
        this
    );
}

